package cn.server.handler;

import static io.netty.handler.codec.http.HttpHeaders.is100ContinueExpected;
import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.util.ReferenceCountUtil;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import cn.server.IBusiness;
import cn.server.util.ServerConfUtil;

public class ReceiveHandler extends ChannelInboundHandlerAdapter {

	private static final Log logger = LogFactory.getLog(ReceiveHandler.class);
	private ByteBuf byteBuffer;
	private boolean keepAlive;
	private final long MAX_LENGTH = 100000000L;
	private Map<String, String> headMap = new HashMap<String, String>();

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if (msg instanceof HttpRequest) {
			HttpRequest req = (HttpRequest) msg;
			if (HttpHeaders.isContentLengthSet(req)) {
				long contentLength = HttpHeaders.getContentLength(req);
				if (contentLength > MAX_LENGTH)
					throw new Exception("max length is too big!");
				this.byteBuffer = ctx.alloc().buffer((int) contentLength);
			}
			if (is100ContinueExpected(req)) {
				ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
			}
			keepAlive = isKeepAlive(req);
			HttpHeaders headers = req.headers();
			Set<String> names = headers.names();
			for (String name : names) {
				headMap.put(name, headers.get(name));
			}
			ReferenceCountUtil.release(req);
		}
		if (msg instanceof HttpContent) {
			try {
				String type = headMap.get("Content-Type");
				if (StringUtils.isBlank(type))
					throw new Exception("no type is set!");
				IBusiness handler = ServerConfUtil.getHandler(type);
				if (handler == null)
					throw new Exception("no handler found to deal type:" + type);
				HttpContent httpContent = (HttpContent) msg;
				ByteBuf content = httpContent.content();
				content.readBytes(this.byteBuffer,content.readableBytes());
				content.release();
				System.out.println(this.byteBuffer.writableBytes());
				if (this.byteBuffer.writableBytes() == 0) {
					byte[] requestData = new byte[this.byteBuffer.readableBytes()];
					this.byteBuffer.readBytes(requestData);
					// TODO 增加接收到的请求处理
					byte[] resp = handler.doBusiness(requestData, headMap);
					DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK,
							Unpooled.wrappedBuffer(resp));
					response.headers().set(CONTENT_TYPE, "text/plain");
					response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
					if (!keepAlive) {
						ctx.write(response).addListener(ChannelFutureListener.CLOSE);
					} else {
						response.headers().set(CONNECTION, Values.KEEP_ALIVE);
						ctx.write(response);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		super.channelReadComplete(ctx);
		ctx.flush();
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		super.exceptionCaught(ctx, cause);
		logger.error("socket deal error!", cause);
		ctx.close();
	}
}
